﻿#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include <pthread.h>
#include "Log.hpp" // 引⼊⾃⼰的⽇志
#include "Thread.hpp" // 引⼊⾃⼰的线程
#include "Lock.hpp" // 引⼊⾃⼰的锁
#include "Cond.hpp" // 引⼊⾃⼰的条件变量
using namespace ThreadModule;
using namespace CondModule;
using namespace LockModule;
using namespace LogModule;
const static int gdefaultthreadnum = 10;
// ⽇志
template <typename T>
class ThreadPool
{
private:
	// 是要有的，必须是私有的
	ThreadPool(int threadnum = gdefaultthreadnum) : _threadnum(threadnum),
		_waitnum(0), _isrunning(false)
	{
		LOG(LogLevel::INFO) << "ThreadPool Construct()";
	}
	void InitThreadPool()
	{
		// 指向构建出所有的线程，并不启动
		for (int num = 0; num < _threadnum; num++)
		{
			_threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this));
			LOG(LogLevel::INFO) << "init thread " << _threads.back().Name() <<
				" done";
		}
	}
	void Start()
	{
		_isrunning = true;
		for (auto& thread : _threads)
		{
			thread.Start();
			LOG(LogLevel::INFO) << "start thread " << thread.Name() << "done";
		}
	}
	void HandlerTask() // 类的成员⽅法，也可以成为另⼀个类的回调⽅法，⽅便我们继续类级
		别的互相调⽤！
	{
		std::string name = GetThreadNameFromNptl();
		LOG(LogLevel::INFO) << name << " is running...";
		while (true)
		{
			// 1. 保证队列安全
			_mutex.Lock();
			// 2. 队列中不⼀定有数据
			while (_task_queue.empty() && _isrunning)
			{
				_waitnum++;
				_cond.Wait(_mutex);
				_waitnum--;
			}
			// 2.1 如果线程池已经退出了 && 任务队列是空的
			if (_task_queue.empty() && !_isrunning)
			{
				_mutex.Unlock();
				break;
			}
			// 2.2 如果线程池不退出 && 任务队列不是空的
			// 2.3 如果线程池已经退出 && 任务队列不是空的 --- 处理完所有的任务，然后在
			退出
				// 3. ⼀定有任务, 处理任务
				T t = _task_queue.front();
			_task_queue.pop();
			_mutex.Unlock();
			LOG(LogLevel::DEBUG) << name << " get a task";
			// 4. 处理任务，这个任务属于线程独占的任务
			t();
		}
	}
	// 复制拷⻉禁⽤
	ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;
	ThreadPool(const ThreadPool<T>&) = delete;
public:
	static ThreadPool<T>* GetInstance()
	{
		// 如果是多线程获取线程池对象下⾯的代码就有问题了！！
		// 只有第⼀次会创建对象，后续都是获取
		// 双判断的⽅式，可以有效减少获取单例的加锁成本，⽽且保证线程安全
		if (nullptr == _instance) // 保证第⼆次之后，所有线程，不⽤在加锁，直接返回
			_instance单例对象
		{
		LockGuard lockguard(_lock);
		if (nullptr == _instance)
		{
		_instance = new ThreadPool<T>();
		_instance->InitThreadPool();
		_instance->Start();
		LOG(LogLevel::DEBUG) << "创建线程池单例";
		return _instance;
		}
		}
		LOG(LogLevel::DEBUG) << "获取线程池单例";
		return _instance;
	}
	void Stop()
	{
		_mutex.Lock();
		_isrunning = false;
		_cond.NotifyAll();
		_mutex.Unlock();
		LOG(LogLevel::DEBUG) << "线程池退出中...";
	}
	void Wait()
	{
		for (auto& thread : _threads)
		{
			thread.Join();
			LOG(LogLevel::INFO) << thread.Name() << " 退出...";
		}
	}
	bool Enqueue(const T& t)
	{
		bool ret = false;
		_mutex.Lock();
		if (_isrunning)
		{
			_task_queue.push(t);
			if (_waitnum > 0)
			{
				_cond.Notify();
			}
			LOG(LogLevel::DEBUG) << "任务⼊队列成功";
			ret = true;
		}
		_mutex.Unlock();
		return ret;
	}
	~ThreadPool()
	{}
private:
	int _threadnum;
	std::vector<Thread> _threads; // for fix, int temp
	std::queue<T> _task_queue;
	Mutex _mutex;
	Cond _cond;
	int _waitnum;
	bool _isrunning;
	// 添加单例模式
	static ThreadPool<T>* _instance;
	static Mutex _lock;
};
template <typename T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;
template <typename T>
Mutex ThreadPool<T>::_lock;